/************************************************************************/
/* test for qrencode                                                                     */
/************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <png.h>
#include <fcntl.h>
#include <io.h>

#include "qrencode.h"

#define INCHES_PER_METER (100.0/2.54)

#if defined(_WIN32) || defined(_WIN64)
#define snprintf _snprintf
//#define vsnprintf _vsnprintf
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#endif

static int casesensitive = 1;
static int eightbit = 0;
static int version = 0;
static int size = 3;
static int margin = -1;
static int dpi = 72;
static int structured = 0;
static int micro = 0;
static QRecLevel level = QR_ECLEVEL_L;
static QRencodeMode hint = QR_MODE_8;
static unsigned int fg_color[4] = {0, 0, 0, 255};
static unsigned int bg_color[4] = {255, 255, 255, 255};

enum imageType {
    PNG_TYPE,
    EPS_TYPE,
    SVG_TYPE,
    ANSI_TYPE,
    ANSI256_TYPE,
    ASCII_TYPE,
    ASCIIi_TYPE,
    UTF8_TYPE,
    ANSIUTF8_TYPE
};

static enum imageType image_type = PNG_TYPE;


static int writePNG(QRcode *qrcode, const char *outfile)
{
    static FILE *fp; // avoid clobbering by setjmp.
    png_structp png_ptr;
    png_infop info_ptr;
    png_colorp palette;
    png_byte alpha_values[2];
    unsigned char *row, *p, *q;
    int x, y, xx, yy, bit;
    int realwidth;

    realwidth = (qrcode->width + margin * 2) * size;
    row = (unsigned char *)malloc((realwidth + 7) / 8);
    if(row == NULL) {
        fprintf(stderr, "Failed to allocate memory.\n");
        exit(EXIT_FAILURE);
    }

    if(outfile[0] == '-' && outfile[1] == '\0') {
        fp = stdout;
        _setmode(_fileno(stdout), O_BINARY);
    } else {
        fp = fopen(outfile, "wb");
        if(fp == NULL) {
            fprintf(stderr, "Failed to create file: %s\n", outfile);
            perror(NULL);
            exit(EXIT_FAILURE);
        }
    }

    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if(png_ptr == NULL) {
        fprintf(stderr, "Failed to initialize PNG writer.\n");
        exit(EXIT_FAILURE);
    }

    info_ptr = png_create_info_struct(png_ptr);
    if(info_ptr == NULL) {
        fprintf(stderr, "Failed to initialize PNG write.\n");
        exit(EXIT_FAILURE);
    }

    if(setjmp(png_jmpbuf(png_ptr))) {
        png_destroy_write_struct(&png_ptr, &info_ptr);
        fprintf(stderr, "Failed to write PNG image.\n");
        exit(EXIT_FAILURE);
    }

    palette = (png_colorp) malloc(sizeof(png_color) * 2);
    palette[0].red   = fg_color[0];
    palette[0].green = fg_color[1];
    palette[0].blue  = fg_color[2];
    palette[1].red   = bg_color[0];
    palette[1].green = bg_color[1];
    palette[1].blue  = bg_color[2];
    alpha_values[0] = fg_color[3];
    alpha_values[1] = bg_color[3];
    png_set_PLTE(png_ptr, info_ptr, palette, 2);
    png_set_tRNS(png_ptr, info_ptr, alpha_values, 2, NULL);

    png_init_io(png_ptr, fp);
    png_set_IHDR(png_ptr, info_ptr,
        realwidth, realwidth,
        1,
        PNG_COLOR_TYPE_PALETTE,
        PNG_INTERLACE_NONE,
        PNG_COMPRESSION_TYPE_DEFAULT,
        PNG_FILTER_TYPE_DEFAULT);
    png_set_pHYs(png_ptr, info_ptr,
        dpi * INCHES_PER_METER,
        dpi * INCHES_PER_METER,
        PNG_RESOLUTION_METER);
    png_write_info(png_ptr, info_ptr);

    /* top margin */
    memset(row, 0xff, (realwidth + 7) / 8);
    for(y=0; y<margin * size; y++) {
        png_write_row(png_ptr, row);
    }

    /* data */
    p = qrcode->data;
    for(y=0; y<qrcode->width; y++) {
        bit = 7;
        memset(row, 0xff, (realwidth + 7) / 8);
        q = row;
        q += margin * size / 8;
        bit = 7 - (margin * size % 8);
        for(x=0; x<qrcode->width; x++) {
            for(xx=0; xx<size; xx++) {
                *q ^= (*p & 1) << bit;
                bit--;
                if(bit < 0) {
                    q++;
                    bit = 7;
                }
            }
            p++;
        }
        for(yy=0; yy<size; yy++) {
            png_write_row(png_ptr, row);
        }
    }
    /* bottom margin */
    memset(row, 0xff, (realwidth + 7) / 8);
    for(y=0; y<margin * size; y++) {
        png_write_row(png_ptr, row);
    }

    png_write_end(png_ptr, info_ptr);
    png_destroy_write_struct(&png_ptr, &info_ptr);

    fclose(fp);
    free(row);

    return 0;
}

static QRcode *encode(const unsigned char *intext, int length)
{
    QRcode *code;

    if(micro) {
        if(eightbit) {
            code = QRcode_encodeDataMQR(length, intext, version, level);
        } else {
            code = QRcode_encodeStringMQR((char *)intext, version, level, hint, casesensitive);
        }
    } else {
        if(eightbit) {
            code = QRcode_encodeData(length, intext, version, level);
        } else {
            code = QRcode_encodeString((char *)intext, version, level, hint, casesensitive);
        }
    }

    return code;
}

static void qrencode(const unsigned char *intext, int length, const char *outfile)
{
    QRcode *qrcode;

    qrcode = encode(intext, length);
    if(qrcode == NULL) {
        perror("Failed to encode the input data");
        exit(EXIT_FAILURE);
    }

    switch(image_type) {
        case PNG_TYPE:
            writePNG(qrcode, outfile);
            break;
        /*
        case EPS_TYPE:
            writeEPS(qrcode, outfile);
            break;
        case SVG_TYPE:
            writeSVG(qrcode, outfile);
            break;
        case ANSI_TYPE:
        case ANSI256_TYPE:
            writeANSI(qrcode, outfile);
            break;
        case ASCIIi_TYPE:
            writeASCII(qrcode, outfile,  1);
            break;
        case ASCII_TYPE:
            writeASCII(qrcode, outfile,  0);
            break;
        case UTF8_TYPE:
            writeUTF8(qrcode, outfile, 0);
            break;
        case ANSIUTF8_TYPE:
            writeUTF8(qrcode, outfile, 1);
            break;
        */
        default:
            fprintf(stderr, "Unknown image type.\n");
            exit(EXIT_FAILURE);
    }

    QRcode_free(qrcode);
}

static QRcode_List *encodeStructured(const unsigned char *intext, int length)
{
    QRcode_List *list;

    if(eightbit) {
        list = QRcode_encodeDataStructured(length, intext, version, level);
    } else {
        list = QRcode_encodeStringStructured((char *)intext, version, level, hint, casesensitive);
    }

    return list;
}

static void qrencodeStructured(const unsigned char *intext, int length, const char *outfile)
{
    QRcode_List *qrlist, *p;
    char filename[FILENAME_MAX];
    char *base, *q, *suffix = NULL;
    const char *type_suffix;
    int i = 1;
    size_t suffix_size;

    switch(image_type) {
        case PNG_TYPE:
            type_suffix = ".png";
            break;
        case EPS_TYPE:
            type_suffix = ".eps";
            break;
        case SVG_TYPE:
            type_suffix = ".svg";
            break;
        case ANSI_TYPE:
        case ANSI256_TYPE:
        case ASCII_TYPE:
        case UTF8_TYPE:
        case ANSIUTF8_TYPE:
            type_suffix = ".txt";
            break;
        default:
            fprintf(stderr, "Unknown image type.\n");
            exit(EXIT_FAILURE);
    }

    if(outfile == NULL) {
        fprintf(stderr, "An output filename must be specified to store the structured images.\n");
        exit(EXIT_FAILURE);
    }
    base = strdup(outfile);
    if(base == NULL) {
        fprintf(stderr, "Failed to allocate memory.\n");
        exit(EXIT_FAILURE);
    }
    /*
    suffix_size = strlen(type_suffix);
    if(strlen(base) > suffix_size) {
        q = base + strlen(base) - suffix_size;
        if(strcasecmp(type_suffix, q) == 0) {
            suffix = strdup(q);
            *q = '\0';
        }
    }
    */

    qrlist = encodeStructured(intext, length);
    if(qrlist == NULL) {
        perror("Failed to encode the input data");
        exit(EXIT_FAILURE);
    }

    for(p = qrlist; p != NULL; p = p->next) {
        if(p->code == NULL) {
            fprintf(stderr, "Failed to encode the input data.\n");
            exit(EXIT_FAILURE);
        }
        if(suffix) {
            snprintf(filename, FILENAME_MAX, "%s-%02d%s", base, i, suffix);
        } else {
            snprintf(filename, FILENAME_MAX, "%s-%02d", base, i);
        }
        switch(image_type) {
            case PNG_TYPE: 
                writePNG(p->code, filename);
                break;
            /*
            case EPS_TYPE: 
                writeEPS(p->code, filename);
                break;
            case SVG_TYPE: 
                writeSVG(p->code, filename);
                break;
            case ANSI_TYPE:
            case ANSI256_TYPE:
                writeANSI(p->code, filename);
                break;
            case ASCIIi_TYPE:
                writeASCII(p->code, filename, 1);
                break;
            case ASCII_TYPE:
                writeASCII(p->code, filename, 0);
                break;
            case UTF8_TYPE:
                writeUTF8(p->code, filename, 0);
                break;
            case ANSIUTF8_TYPE:
                writeUTF8(p->code, filename, 0);
                break;
            */
            default:
                fprintf(stderr, "Unknown image type.\n");
                exit(EXIT_FAILURE);
        }
        i++;
    }

    free(base);
    if(suffix) {
        free(suffix);
    }

    QRcode_List_free(qrlist);
}

int main(int argc, char **argv)
{
    int opt, lindex = -1;
    char *outfile = NULL;
    unsigned char *intext = NULL;
    int length = 0;


    char* content = "PCIP:192.168.0.100:5957:1";
    intext = (unsigned char*)content;
    outfile = "./test.png";
    size = 3;
    margin = 3;
    level = QR_ECLEVEL_L;

    if(structured) {
        if(version == 0) {
            fprintf(stderr, "Version must be specified to encode structured symbols.\n");
            exit(EXIT_FAILURE);
        }
        qrencodeStructured(intext, length, outfile);
    } else {
        qrencode(intext, length, outfile);
    }

    return 0;
}